옵저버 패턴이란
옵저버 패턴이란?
옵저버 패턴이란
옵저버 패턴은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴
- 한 객체의 상태 변화에 따라 다른 객체의 상태도 연동되도록 일대다 객체 의존 관계를 구성하는 패턴
- 데이터의 변경이 발생했을 경우 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용하다.
- ex) 새로운 파일이 추가되거나 기존 파일이 삭제되었을 때 탐색기는 다른 탐색기에게 즉시 변경을 통보해야한다.
- 옵저버 패턴은 통보 대상 객체의 관리를 Subject 클래스와 Observer 인터페이스로 일반화한다.
- 이를 통해 데이터 변경을 통보하는 클래스(ConcreteSubject)는 통보 대상 클래스나 객체에(ContcreteObserver)에 대한 의존성을 없앨 수 있다.
- 결과적으로 통보 대상 클래스나 대상 객체의 변경에도 통보하는 클래스(ConcreteSubject)를 수정 없이 그대로 사용할 수 있다.
역할이 수행하는 작업
- Observer
- 데이터의 변경을 통보 받는 인터페이스
- 즉, Subject에서는 Observer 인터페이스의 update메서드를 호출함으로써 ConcreteSubject의 데이터 변경을 ConcreteObserver에게 통보한다.
- Subject
- ConcreteObserver 객체를 관리하는 요소
- Observer 인터페이스를 참조해서 ConcreteObserver를 관리하므로 ConcreteObserver의 변화에 독립적일 수 있다.
- ConcreteSubject
- 변경 관리 대상이 되는 데이터가 있는 클래스(통보하는 클래스)
- 데이터의 변경을 위한 메서드인 setState가 있다.
- setState 메서드에서는 자신의 데이터인 subjectState를 변경하고 Subject의 notifyObservers 메서드를 호출해서 ConcreteObserver 객체에 변경을 통보한다.
- ConcreteObserver
- ConcreteSubject의 변경을 통보받는 클래스
- Observer인터페이스의 update 메서드를 구현함으로써 변경을 통보받는다.
- 변경된 데이터는 ConcreteSubject의 get State메서드를 호출함으로써 변경을 조회한다.
간단한 코드 예시
interface Subject{
public void attach(Observer o); // Subject에 Observer를 구독자로 등록한다.
public void detach(Observer o); // Subject에 등록한 Observer의 구독을 해지한다.
public void notify(); // Subject에서 모든 Observer에 정보를 전달한다.
}
class SubjectImpl implements Subject{
private List<Observer> observers= new ArrayList<>();
private Data data1;
private Data data2;
public void attach(Observer o){
observers.add(o);
}
public void detach(Observer o){
observers.remove(o);
}
public void notify(){
//모든 옵저버를 순회하며 업데이트 적용
for (Observer o: observers){
o.update(this);
}
}
public void setData1(Data d){
this.data1=d;
}
}
주의할점
옵저버는 상태를 갖지 않아도 된다.
- 상태는 Subject의 담당이므로 Subject와 Observer의 일대다 관계가 성립한다.
- 예제에서는 Observer가 update를 통해 값을 전달받고 저장하지만, 상태를 굳이 저장할 필요가 없는 경우에는 상태를 저장하지 않아도 된다.
Notify를 누가 호출해야할지
- GoF는 다음 두 가지 방법 중에서 선택하라고 권유한다.
- Subject에서 변경이 발생할 때, 변경을 저장하는 메소드가 Notify()를 호출하는 방법.
- 사용자(main 등)가 적절한 시기에 Notify()를 호출하는 방법.
- GoF는 다음 두 가지 방법 중에서 선택하라고 권유한다.
Observer의 행위가 Subject에 영향을 주는 경우
만약 Observer의 행위가 Subject에 영향을 주는 로직이 있다면, 무한 루프가 발생할 수 있으므로 주의할 필요가 있다.
- Subject가 notify를 호출한다.
- Observer의 update가 호출된다.
- Observer::update 실행도중 Subject에 영향을 준다.
- Goto 1
옵저버패턴의 장단점
장점
- 실시간으로 한 객체의 변경사항을 다른 객체에 전파할 수 있습니다.
- 느슨한 결합으로 시스템이 유연하고 객체간의 의존성을 제거할 수 있다.
단점
- 너무 많이 사용하게 되면, 상태 관리가 힘들 수 있습니다.
- 데이터 배분에 문제가 생기면 자칫 큰 문제로 이어질 수 있습니다.
관련 면접질문
옵저버패턴을 사용하는 이유는 무엇인가요?
-> Subject와 observer가 느슨하게 결합되어 있는 객체 디자인을 제공할 수 있다. 그로인해 옵저버의 추가, 제거가 자유로워 지고 새로운 형식의 옵저버를 추가하기도 쉬워진다. 예를 들면 Android의 EventListener처럼 대부분의 이벤트들이 옵저버 패턴 방식으로 사용되고 있다.
참고 자료:
기여자
Youngwoo Kim
📦